package com.neoCalf.neoUser.webSocket.tool;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.neoCalf.neoCommon.entity.ChatMessage;
import com.neoCalf.neoCommon.entity.Session;
import com.neoCalf.neoCommon.entity.UserContactApply;
import com.neoCalf.neoCommon.entity.UserInfo;
import com.neoCalf.neoCommon.enums.MessageTypeEnum;
import com.neoCalf.neoCommon.enums.UserContactTypeEnum;
import com.neoCalf.neoCommon.model.R;
import com.neoCalf.neoCommon.model.dto.SendMessageDto;
import com.neoCalf.neoCommon.model.vo.WsInitData;
import com.neoCalf.neoCommon.redis.RedisComponent;
import com.neoCalf.neoCommon.utils.DataTool;
import com.neoCalf.neoCommon.utils.JsonUtils;
import com.neoCalf.neoUser.feigns.ChatServiceFeign;
import com.neoCalf.neoUser.feigns.ContactServiceFeign;
import com.neoCalf.neoUser.mapper.UserInfoMapper;
import com.neoCalf.neoUser.pro.SocketPro;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;


@Component
public class ChannelContextUtils {
    @Resource
    private RedisComponent<String> redisComponent;
    @Resource
    private UserInfoMapper userInfoMapper;
    @Resource
    private WsDataTool dataTool;
    @Resource
    private SocketPro socketPro;
    @Resource
    private ChatServiceFeign chatServiceFeign;
    @Autowired
    private ContactServiceFeign contactServiceFeign;

    private static final ConcurrentHashMap<String, Channel> USER_CONTACT_MAP = new ConcurrentHashMap<>();
    private static final ConcurrentHashMap<String, ChannelGroup> GROUP_CONCURRENT_MAP = new ConcurrentHashMap<>();

    public void logout(String userId) {
        removeContext(USER_CONTACT_MAP.get(userId));
        LambdaUpdateWrapper<UserInfo> userInfoLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        userInfoLambdaUpdateWrapper.eq(UserInfo::getUserId, userId);
        userInfoLambdaUpdateWrapper.set(UserInfo::getLastOffTime, new Date().getTime());
        userInfoMapper.update(null, userInfoLambdaUpdateWrapper);
    }

    /**
     * 将channelId与userId绑定
     */
    public void addContact(String userId, String token, ChannelHandlerContext ctx) {
        Channel incoming = ctx.channel();
        String incomingId = incoming.id().toString();
        AttributeKey<String> attributeKey;
        if (!AttributeKey.exists(incomingId)) {
            attributeKey = AttributeKey.newInstance(incomingId);
        } else {
            attributeKey = AttributeKey.valueOf(incomingId);
        }
        incoming.attr(attributeKey).set(userId + "\n" + token);

        // 初始化添加channel映射
        USER_CONTACT_MAP.put(userId, incoming);

        List<String> groupIdList = redisComponent.getGroupList(userId);
        for (String groupId : groupIdList) {
            addChannelToGroup(groupId, incoming);
        }

        // 查询用户所有会话消息 保证了设备会话同步
        R<List<Session>> sessionListR = chatServiceFeign.findSession(userId);
        // 查询聊天信息
        List<String> sessionIdList = sessionListR.getResult().stream().map(Session::getSessionId).collect(Collectors.toList());
        List<ChatMessage> chatMessageList = new ArrayList<>();
        if (!sessionIdList.isEmpty()) {
            chatMessageList = chatServiceFeign.findChatMessageList(sessionIdList).getResult();
        }

        int initStatus = MessageTypeEnum.INIT.getType();
        // 查询好友申请
        List<UserContactApply> userContactApplyList = contactServiceFeign.selectUserContactApplyList(userId).getResult();

        WsInitData wsInitData = new WsInitData(sessionListR.getResult(), chatMessageList, userContactApplyList);
        //发送消息
        SendMessageDto<WsInitData> sendMessageDto = new SendMessageDto<>();
        sendMessageDto.setMessageType(initStatus);
        sendMessageDto.setExtendData(wsInitData);

        sendMessageToUser(sendMessageDto, userId);
    }

    public <T> void sendMessageToUser(SendMessageDto<T> sendMessageDto, String receiveId) {
        Channel channel = USER_CONTACT_MAP.get(receiveId);
        if (DataTool.isNull(channel)) {
            return;
        }
        channel.writeAndFlush(new TextWebSocketFrame(JsonUtils.convertObjToJson(sendMessageDto)));
    }

    public <T> void sendMessageToGroup(SendMessageDto<T> sendMessageDto, String groupId) {
        ChannelGroup channels = GROUP_CONCURRENT_MAP.get(groupId);
        if (DataTool.isNull(channels)) {
            return;
        }
        channels.writeAndFlush(new TextWebSocketFrame(JsonUtils.convertObjToJson(sendMessageDto)));
    }

    public void addChannelToGroup(String groupId, Channel channel) {
        if (channel == null) {
            return;
        }
        ChannelGroup channels = GROUP_CONCURRENT_MAP.get(groupId);
        if (channels == null) {
            channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
            GROUP_CONCURRENT_MAP.put(groupId, channels);
        }
        channels.add(channel);
    }

    public void addChannelByUserIdToGroup(String groupId, String userId) {
        Channel channel = USER_CONTACT_MAP.get(userId);
        addChannelToGroup(groupId, channel);
    }

    /**
     * 清理用户通道
     */
    public void removeContext(Channel channel) {
        if (channel == null) {
            return;
        }
        String userId = dataTool.getUserIdByChannel(channel);
        if (userId == null || userId.isEmpty()) {
            return;
        }
        USER_CONTACT_MAP.remove(userId);
        dataTool.delUserHeartBeat(userId);

        redisComponent.delContactList(userId);
        channel.close();
    }

    /**
     * 消息选发
     */
    public <T> void sendMessageMethod(SendMessageDto<T> sendMessageDto) {
        String receiveId = sendMessageDto.getContactId();
        UserContactTypeEnum typeEnum = UserContactTypeEnum.getByType(sendMessageDto.getContactType());
        switch (typeEnum) {
            case USER:
            case SYSTEM:
            case ROBOT:
                sendMessageToUser(sendMessageDto, receiveId);
                break;
            case GROUP:
                sendMessageToGroup(sendMessageDto, receiveId);
                break;
        }
    }
}
